﻿' This software is licensed under Microsoft Public License (Ms-PL), which governs use of the accompanying software. 
' If you use the software, you accept this license. 

' The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, 
' guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot 
' change. To the extent permitted under your local laws, the contributors exclude the implied warranties of 
' merchantability, fitness for a particular purpose and non-infringement.

Imports System.ServiceModel
Imports System.ServiceModel.Web
Imports System.ServiceModel.Description

Namespace Rest
    ''' <summary>
    ''' A REST derived class that compliments the Windows Communication Foundation (WCF) programming model.
    ''' </summary>
    ''' <typeparam name="InstanceType">Interface type of the REST service to implement.</typeparam>
    ''' <typeparam name="Instance">Instance of the REST service.</typeparam>
    ''' <remarks></remarks>
    Public Class RestServiceHost(Of InstanceType, Instance)

        Private _host As WebServiceHost
        Private _serviceUri As Uri

#Region "Constructor"
        ''' <summary>
        ''' Initializes a new instance of the RestServiceHost class.
        ''' </summary>
        ''' <param name="serviceUri">URI of the REST service implementation.</param>
        ''' <remarks></remarks>
        Public Sub New(ByVal serviceUri As Uri)
            _host = New WebServiceHost(GetType(Instance))

            _serviceUri = serviceUri
        End Sub

        ''' <summary>
        ''' Initializes a new instance of the RestServiceHost class; from an singleton instance.
        ''' </summary>
        ''' <param name="serviceUri"></param>
        ''' <param name="singletonInstance"></param>
        ''' <remarks></remarks>
        Public Sub New(ByVal serviceUri As Uri, ByVal singletonInstance As Instance)
            _host = New WebServiceHost(singletonInstance)
            _serviceUri = serviceUri
        End Sub
#End Region

#Region "Events"
        ''' <summary>
        ''' Fired upon unhandled errors from the WCF REST runtime.
        ''' </summary>
        ''' <param name="ex"><see cref="Exception">Exception</see> raised.</param>
        ''' <remarks></remarks>
        Public Event OnError(ByVal ex As Exception)
#End Region

#Region "Public Methods"
        ''' <summary>
        ''' Opens the Service Host and waits for connections.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Open()
            Dim endpoint As ServiceEndpoint = _host.AddServiceEndpoint(GetType(InstanceType), GetBinding, _serviceUri)
            endpoint.Behaviors.Add(GetHttpBehavior())
#If DEBUG Then
            Dim debugBehavior As ServiceDebugBehavior = _host.Description.Behaviors.Find(Of ServiceDebugBehavior)()
#End If

            If Me.AuthorizationManager IsNot Nothing Then
                ' _host.Authorization.PrincipalPermissionMode = PrincipalPermissionMode.Custom
                _host.Authorization.ServiceAuthorizationManager = Me.AuthorizationManager
            End If

            Try
                _host.Open()
            Catch ex As Exception
                Throw
            End Try
        End Sub

        ''' <summary>
        ''' Closes the Service Host and connections.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Close()
            _host.Close()
        End Sub
#End Region

#Region "Public Properties"
        ''' <summary>
        ''' Gets or sets a value that determines if the WCF Help page is enabled for REST.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property HelpPageEnabled As Boolean = False

        ''' <summary>
        ''' Gets a value that indicates the current state of the communication object.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property State As CommunicationState
            Get
                Return _host.State
            End Get
        End Property

        ''' <summary>
        ''' Gets or sets a value, overriding the ServiceAuthorizationManager.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property AuthorizationManager As ServiceAuthorizationManager = Nothing

        ''' <summary>
        ''' Gets or sets a value that indicates if the REST service host is using Streamed Transfer mode for both request and response.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property IsStreamedTransfer As Boolean = False

        ''' <summary>
        ''' Gets or sets a value of the maximum buffer to read and submit.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property MaxTransferBuffer As Long = 65556
#End Region

#Region "Private Methods"
        ''' <summary>
        ''' Handles errors recieved from the RestHttpBehavior.
        ''' </summary>
        ''' <param name="e">Exception to handle.</param>
        ''' <remarks></remarks>
        Private Sub HandleError(ByVal e As Exception)
            RaiseEvent OnError(e)
        End Sub
#End Region

#Region "Protected methods"
        ''' <summary>
        ''' Returns a new instance of the RestHttpBehavior.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Protected Friend Overridable Function GetHttpBehavior() As RestHttpBehavior
            Dim behavior As RestHttpBehavior = New RestHttpBehavior With {
                .HelpEnabled = Me.HelpPageEnabled
            }
            AddHandler behavior.OnErrorCapture, AddressOf HandleError

            Return behavior
        End Function

        ''' <summary>
        ''' Use this method to override any binding initialization.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Protected Friend Overridable Function GetBinding() As WebHttpBinding
            Dim binding As WebHttpBinding = New WebHttpBinding()
            binding.AllowCookies = True
            binding.ReaderQuotas.MaxArrayLength = Me.MaxTransferBuffer
            binding.ReaderQuotas.MaxBytesPerRead = Me.MaxTransferBuffer
            binding.ReaderQuotas.MaxStringContentLength = Me.MaxTransferBuffer

            ' set transfer mode
            If Me.IsStreamedTransfer Then binding.TransferMode = TransferMode.Streamed

            Return binding
        End Function
#End Region
    End Class
End Namespace
